// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#ifndef STORAGE_LEVELDB_DB_VERSION_EDIT_H_
#define STORAGE_LEVELDB_DB_VERSION_EDIT_H_

#include <set>
#include <utility>
#include <vector>

#include "db/dbformat.h"

namespace leveldb {

class VersionSet;

struct FileMetaData {
  FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) {}

  int refs;  // 表示当前文件被引用的次数，如果该值为 0，则表示该文件可以被删除。
  int allowed_seeks;  // 每个文件允许的最大无效查询次数。该变量是触发miss seek compaction的依据。无效读取数超过一定限制时，就会将file_to_compact_变量置为f1，file_to_compact_level_置为f1所属于的层级n。
  uint64_t number;  // 表示文件的序列号，唯一标识一个 SST 文件。
  uint64_t file_size;    // 表示该文件的大小，单位为字节。
  InternalKey smallest;  // 表示文件最小的key值。
  InternalKey largest;   // 表示文件最大的key值。
};

/*
 * VersionEdit是一个版本的中间状态，会保存一次Compaction操作后增加的删除文件信息以及其他一些元数据。
 * 这个VersionEdit对象被用来描述在当前版本集合上的更改操作，并与当前的Version对象进行合并，生成一个新的版本集合。
 * 通过将新的VersionEdit对象与当前的Version对象合并，得到的新版本集合包含了最新的键值对和元数据信息。这个过程中，所有的更改都是以增量方式进行的，即旧的版本集合不会被直接修改，而是根据新的更改操作，生成一个全新的版本集合。
 * 因此，VersionEdit的作用是帮助跟踪和记录版本集合的变化情况，并用于标记要添加或删除的SST文件，然后将其与当前Version对象进行合并，生成一个新的版本集合。
 */
class VersionEdit {
 public:
  VersionEdit() { Clear(); }
  ~VersionEdit() = default;

  void Clear();
 
  // 设置比较器名称。
  void SetComparatorName(const Slice& name) {
    has_comparator_ = true;
    comparator_ = name.ToString();
  }
 
  // 设置日志文件序列号。
  void SetLogNumber(uint64_t num) {
    has_log_number_ = true;
    log_number_ = num;
  }
  
  // 已经废弃，代码保留是为了兼容旧版本的 LevelDB。
  void SetPrevLogNumber(uint64_t num) {
    has_prev_log_number_ = true;
    prev_log_number_ = num;
  }

  // 设置下一个文件序列号。
  void SetNextFile(uint64_t num) {
    has_next_file_number_ = true;
    next_file_number_ = num;
  }

  // 设置写入序列号。
  void SetLastSequence(SequenceNumber seq) {
    has_last_sequence_ = true;
    last_sequence_ = seq;
  }

  // 设置指定层级上要进行下一次 compaction 的起始 key。
  void SetCompactPointer(int level, const InternalKey& key) {
    compact_pointers_.push_back(std::make_pair(level, key));
  }

  // Add the specified file at the specified number.
  // REQUIRES: This version has not been saved (see VersionSet::SaveTo)
  // REQUIRES: "smallest" and "largest" are smallest and largest keys in file
  // 添加指定文件到指定层级中。
  void AddFile(int level, uint64_t file, uint64_t file_size,
               const InternalKey& smallest, const InternalKey& largest) {
    // 将参数中的信息复制到一个FileMetaData结构体并且压入new_files_成员变量。
    FileMetaData f;
    f.number = file;
    f.file_size = file_size;
    f.smallest = smallest;
    f.largest = largest;
    new_files_.push_back(std::make_pair(level, f));
  }

  // Delete the specified "file" from the specified "level".
  // 删除指定层级上的指定文件。
  void RemoveFile(int level, uint64_t file) {
    deleted_files_.insert(std::make_pair(level, file));  // 将指定层级上的指定文件放入deleted_files_成员变量
  }

  // 将 VersionEdit 编码成字符串数据并存储到 dst 中。
  void EncodeTo(std::string* dst) const;
  //从一个Slice中解码为VersionEdit结构。
  Status DecodeFrom(const Slice& src);

  // 返回 VersionEdit 对象的调试信息。
  std::string DebugString() const;

 private:
  friend class VersionSet;

  typedef std::set<std::pair<int, uint64_t>> DeletedFileSet;

  std::string comparator_;  // 比较器的名称，这个在创建 LevelDB 的时候就确定了，以后都不能修改。
  uint64_t log_number_;  // 最小的有效 log number。小于 log_numbers_ 的 log 文件都可以删除。日志文件的名称由6位（不足6位则前边加0补充）日志文件序列号加log后缀组成，例如000001.log。
  uint64_t prev_log_number_;  // 已经废弃，代码保留是为了兼容旧版本的 LevelDB。
  uint64_t next_file_number_;  // 下一个文件序列号。LevelDB中的文件包括日志文件以及SSTable文件、Manifest文件。SSTable文件由6位文件序列号加ldb后缀组成，如000002.ldb；Manifest文件由Manifest-加6位文件序列号组成，例如Manifest-000003。所有文件的序列号顺序递增。
  SequenceNumber last_sequence_;  // 下一个写入序列号。LevelDB的每次写入都会递增序列号。
  bool has_comparator_;  // 标记是否设置了比较器名称。
  bool has_log_number_;  // 标记是否设置了log_number_。
  bool has_prev_log_number_;  // 已经废弃，标记是否设置了prev_log_number。
  bool has_next_file_number_;  // 标记是否设置了next_file_number_。
  bool has_last_sequence_;  // 标记是否设置了last_sequence_。

  std::vector<std::pair<int, InternalKey>> compact_pointers_;  // 该变量用来指示LevelDB中每个层级下一次进行Compaction操作时需要从哪个键开始。对每个层级L，会记录该层上次进行Compaction操作时的最大键，当L层下一次进行Compaction操作需要选取文件时，该文件的最小键需要大于记录的最大键。即每一层的Compaction操作都会在该层的键空间循环执行。
  DeletedFileSet deleted_files_;  // 记录每个层级执行Compaction操作之后删除掉的文件（level-no -> file-no）
  std::vector<std::pair<int, FileMetaData>> new_files_;  // 记录每个层级执行Compaction操作之后新增的文件，注意新增文件记录为一个个FileMetaData结构体。std::pair<int, FileMetaData>中的int表示leveldb中的level。
};

}  // namespace leveldb

#endif  // STORAGE_LEVELDB_DB_VERSION_EDIT_H_
